#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "./core/core.h"
#include "./core/config.h"
#include "./perips/rom.h"
#include "./perips/ram.h"
#include "./perips/uart.h"
#include "./bus/xbar.h"


int main(int argc, char **argv)
{
    uint32_t instr;
    FILE *bin_file_fp;
    uint8_t *p, *p_bak;
    uint32_t i;
    long size;
    uint32_t addr;
    core_t *core;

    // 检查参数
    if (argc < 2) {
        printf("%s, Usage: %s bin_file_path", __func__, argv[0]);
        return -1;
    }

    // 打开bin文件
    printf("%s, opening %s\n", __func__, argv[1]);
    bin_file_fp = fopen(argv[1], "rb");
    //bin_file_fp = fopen("rv32ui-p-add.bin", "rb");
    if (bin_file_fp == NULL) {
        printf("%s, can not open %s\n", __func__, argv[1]);
        return -1;
    }

    // 获取bin文件大小
    fseek(bin_file_fp, 0, SEEK_END);
    size = ftell(bin_file_fp);
    fseek(bin_file_fp, 0, SEEK_SET);
    printf("%s, bin file size = %ld\n", __func__, size);

    // 分配内存，准备读取bin文件内容
    p = (uint8_t *)calloc(size, 1);
    p_bak = p;
    if (p == NULL) {
        printf("%s, can not alloc mem\n", __func__);
        fclose(bin_file_fp);
        return -1;
    }

    // 读取bin文件
    size = fread(p, 1, size, bin_file_fp);
    fclose(bin_file_fp);

    // 初始化外设
    printf("init perips\n");
    rom_init();
    ram_init();
    uart_init();

    // 加载bin文件内容到rom
    addr = CPU_RESET_ADDR;
    printf("loading bin file into addr: 0x%08x\n", addr);
    for (i = 0; i < size; i += 4) {
        xbar_master_write(addr, 0xf, *((uint32_t *)p));
        //printf("write addr[0x%x]=0x%08x\n", addr, *((uint32_t *)p));
        p += 4;
        addr += 4;
    }
    if (p_bak != NULL) {
        free(p_bak);
        p_bak = NULL;
        p = NULL;
    }

    // 初始化处理器核
    core = core_init(CPU_RESET_ADDR);
    // 开始执行
    while (core->get_gpr_reg(26) != 1) {
        // 取指
        instr = core->fetch_instr();
        // 执行
        core->exec_instr(instr);
    }
    // 判断执行结果
    if (core->get_gpr_reg(27) == 1) {
        printf("---TEST PASS---\n");
    } else {
        printf("!!!TEST FAIL!!!\n");
        printf("fail num = %d\n", core->get_gpr_reg(3));
        for (i = 0; i < 32; i++)
            printf("reg[%d] = 0x%08x\n", i, core->get_gpr_reg(i));
    }

    return 0;
}
